package cc.realxyq.demo.component;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 *  包扫描 方式通过组件注入的时候的，BeanNameGenerator 自定义规则
 *  如果设置了value对应的值，则在后面加Custom ，未指定value的使用默认生成策略 realxyq+不包含包名的类名。
 *
 * @author kwhan
 * @date 2022年01月09日 13:24
 */
public class MyBeanNameGenerator implements BeanNameGenerator {

    private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";

    private final Map<String, Set<String>> metaAnnotationTypesCache = new ConcurrentHashMap<>();

    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {

            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            // 配置了 value= “xxx”，且 xxx不为空串的时候
            if (StringUtils.hasText(beanName)) {
                // 显示配置的beanName，严格准守
                return beanName + "Custom";
            }
        }
        // Fallback: generate a unique default bean name.
        return buildDefaultBeanName(definition, registry);
    }

    /**
     *  就是检查 注解中 中有没有自定义 name 属性
     * @param definition 注解的配置类
     * @return BeanDefinition的 名称
     */
    private String determineBeanNameFromAnnotation(AnnotatedBeanDefinition definition) {
        AnnotationMetadata amd = definition.getMetadata();
        Set<String> types = amd.getAnnotationTypes();
        String beanName = null;
        for (String type : types) {
            AnnotationAttributes attributes = AnnotationAttributes.fromMap(amd.getAnnotationAttributes(type,false));
            // 如果注解属性值为null，直接返回
            if (attributes ==null) {
                return beanName;
            }
            //
            Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
                // 元注释类型名称，如果找不到，则为空集
                Set<String> result = amd.getMetaAnnotationTypes(key);
                return (result.isEmpty() ? Collections.emptySet() : result);
            });

            if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
                Object value = attributes.get("value");
                if (value instanceof String) {
                    String strVal = (String) value;
                    if (StringUtils.hasLength(strVal)) {
                        if (beanName != null && !strVal.equals(beanName)) {
                            throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
                                    "component names: '" + beanName + "' versus '" + strVal + "'");
                        }
                        beanName = strVal;
                    }
                }
            }
        }
        return beanName;
    }

    /**
     *  看看注解类型是否为符合 {... 'value':"myCustom" ...} 的@Component 或者其变种，
     *  或者 javax.annotation.ManagedBean 或者 javax.inject.Named
     *
     * @param annotationType 注解类型
     * @param metaAnnotationTypes 注解元数据类型
     * @param attributes 表示 注释属性键值对的 LinkedHashMap 子类
     * @return  就是看注解类似是不是@Component 或者是不是@Component 的变种，就是组合注解中包含component
     *          注解类型是不是 JSR-250 || JSR-330 的注解,
     *          如果包含其中的类型 并且注解属性的键值对不为null，并且 key中包含 ‘value’ 的配置
     *          举例：@Component(... value = "") 返回true
     */
    private boolean isStereotypeWithNameValue(String annotationType, Set<String> metaAnnotationTypes, AnnotationAttributes attributes) {
        boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
                metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
                annotationType.equals("javax.annotation.ManagedBean") ||
                annotationType.equals("javax.inject.Named");

        return (isStereotype && attributes != null && attributes.containsKey("value"));
    }

    private String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        String beanClassName = definition.getBeanClassName();
        Assert.state(beanClassName != null, "此bean定义的当前bean类名 不存在 ");
        // 不带包名的类名
        String shortClassName = ClassUtils.getShortName(beanClassName);

        // 返回自定义 默认BeanName的生成策略
        return "realxyq" + shortClassName;

        // java内省工具，将字符串的首字母转为小写，但是在首字母和第二个字母都是大写的时候不做处理，User->user but URL -> URL
        // 'return Introspector.decapitalize(shortClassName);'
    }
}
